
module fcgi.jtype ;


package 
	import
	dmdscript.darray,
	dmdscript.script,
	dmdscript.program,
	dmdscript.errmsgs,
	dmdscript.value,
	dmdscript.dobject,
	dmdscript.dfunction,
	dmdscript.property,
	dmdscript.threadcontext,
	dmdscript.dnative;

package import
	std.stdio,
	std.exception,
	std.c.stdlib,
	std.path,
	std.file,
	std.traits;

package import std.stdio;
public void log( string file= __FILE__, int line = __LINE__, T...)(T t){//
	std.stdio.write(file, "(", line, ") ",  t, "\n");
}

class JType(Base) : Dobject {
	
	static if( is(Base==struct) ){
		alias Base* pThis;
	}else static if( is(Base==class) ){
		alias Base pThis;
	} else {
		static assert(false);
	}
	alias FieldTypeTuple!(Base) TupleList ;

	static {
		const string ClassName	= "D_" ~ Base.stringof ;
		int[char[]] tuple_id ;
		Program program = null ;
		Obj_Constructor		Obj_constructor	= null ;
		Obj_Prototype		Obj_prototype	= null ;
		NativeFunctionData 	static_nfd[] ;
		NativeFunctionData 	protype_nfd[] ;
	}
	
	static class Obj_Constructor  : Dfunction {
		this() {
			super(Base.tupleof.length , Dfunction_prototype);
			name = ClassName ;
			
			DnativeFunction.init(this, static_nfd, DontEnum);
		}
		
		void* Construct(CallContext *cc, Value *ret, Value[] arglist) {
			assert(false,  ClassName ~ " can't call Constructor");
			return null;
		}

		void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
		{
			assert(false,  ClassName ~ " can't call Constructor");
			return null;
		}
	}
	
	static class Obj_Prototype : Dobject {
		Dobject	properties;
		Value[Base.tupleof.length ] _tuple ;
		
		this() {
			super(Dobject_prototype);
			Put( ClassName, cast(Dobject) Obj_constructor, DontEnum);
			
			DnativeFunction.init(this, static_nfd, DontEnum);
			assert(proptable.get("toString", Value.calcHash("toString")));
			
			properties	= new Dobject( Dobject.getPrototype) ;
			Put("tupleof", properties, DontEnum | DontDelete | ReadOnly);
			
			Put("ClassName",  ClassName, DontEnum | DontDelete | ReadOnly);
			
			foreach(int i, _type; TupleList) {
				const name	= Base.tupleof[i].stringof[ Base.stringof.length + 3.. $] ;
				tuple_id[name] = i ;
				static if( isNumeric!(_type) ) {
					_tuple[i].putVnumber(d_number.init);
				} else static if( isSomeString!(_type) ) {
					_tuple[i].putVstring(null);
				} else {
					static assert(false, _type.stringof ) ;
				}
				properties.Put(name, &_tuple[i], DontDelete | ReadOnly);
			}
		}
		
	}
	
	static void Init(Program p) {
		if( Obj_constructor is null ) {
			Obj_constructor	= new Obj_Constructor() ;
			Obj_prototype	= new Obj_Prototype() ;
		}
		if( program is null ){
			program	= p ;
			p.callcontext.global.Put( ClassName, Obj_constructor, DontEnum | DontDelete | ReadOnly ) ;
		}
	}

	pThis	_obj	= null ;
	Value[Base.tupleof.length ] _tuple ;
	
	this(Dobject prototype)
	{
		assert(prototype is Dobject_prototype);
		super(prototype);
		classname = ClassName ;
	}
	
	this(pThis obj)
	{
		super(Obj_prototype);
		classname = ClassName ;
		_obj	= obj ;
	}
	
	Value* Get(d_string PropertyName, uint hash){
		int* id_ptr     = PropertyName in tuple_id;
		if( id_ptr is null ) {
			return super.Get(PropertyName, hash) ;
		}
		int id		= *id_ptr;
		auto ret	= &_tuple[id] ;
		static if(is(Base==class)){
			if( _obj is null ){
				return ret;
			}
		}
		if( ret.isUndefined ) {
			load_value(id);
		}
		return  ret ;
	}
	
	private void load_value(int id) {
		foreach(int i, _type; TupleList) {
			const name	= Base.tupleof[i].stringof[ Base.stringof.length + 3.. $] ;
			if( id is i ) {
				static if( isNumeric!(_type) ) {
					_tuple[i].putVnumber( _obj.tupleof[i]  ) ;
				} else static if( isSomeString!(_type) ) {
					_tuple[i].putVstring( _obj.tupleof[i]  ) ;
				} else {
					static assert(false, _type.stringof ) ;
				}
			}
		}
	}
}
